#![no_std]
#![no_main]
#![feature(alloc_error_handler)]

extern crate alloc;
mod common;

use common::logger::Logger;
use core::mem::MaybeUninit;
use core::time::Duration;
use cortex_m_rt::entry;
use embedded_hal::digital::v2::{InputPin, OutputPin};
use log::{LevelFilter, *};
use stm32f1xx_hal::{
    adc::Adc,
    gpio::{gpioc::PC13, *},
    pac::{self, interrupt, Interrupt},
    prelude::*,
    serial::{Config, Serial},
    timer::{CountDownTimer, Event, Timer},
};
use task_stream::TaskStream;

static mut LED1: MaybeUninit<PC13<Output<OpenDrain>>> = MaybeUninit::uninit();
static mut TIMER: MaybeUninit<CountDownTimer<pac::TIM2>> = MaybeUninit::uninit();

#[interrupt]
fn TIM2() {
    task_stream::tick(100);
    let _err = unsafe { (TIMER.as_mut_ptr().as_mut().unwrap()).wait() };
}

fn led_on() {
    let _err = unsafe { (LED1.as_mut_ptr().as_mut().unwrap()).set_low() };
}
fn led_off() {
    let _err = unsafe { (LED1.as_mut_ptr().as_mut().unwrap()).set_high() };
}

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    let _cp = cortex_m::Peripherals::take().unwrap();
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);

    common::init_alloc();
    // bsp
    let led = gpioc.pc13.into_open_drain_output(&mut gpioc.crh);
    unsafe { LED1.as_mut_ptr().write(led) };

    // logger
    let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
    let rx = gpioa.pa10;
    let serial = Serial::usart1(
        dp.USART1,
        (tx, rx),
        &mut afio.mapr,
        Config::default().baudrate(115200.bps()),
        clocks,
        &mut rcc.apb2,
    );
    Logger::start(serial, LevelFilter::Trace);
    info!("start.");

    // random
    let adc = Adc::adc1(dp.ADC1, &mut rcc.apb2, clocks);
    common::random::init(adc);

    // set timer
    let mut timer = Timer::tim2(dp.TIM2, &clocks, &mut rcc.apb1).start_count_down(10.hz());
    timer.listen(Event::Update);
    unsafe {
        cortex_m::peripheral::NVIC::unmask(Interrupt::TIM2);
    }
    unsafe { TIMER.as_mut_ptr().write(timer) };

    task_stream::spawn(async {
        loop {
            led_on();
            task_stream::sleep(Duration::from_millis(600)).await;
            led_off();
            task_stream::sleep(Duration::from_millis(300)).await;
            led_on();
            task_stream::sleep(Duration::from_millis(500)).await;
            led_off();
            task_stream::sleep(Duration::from_millis(200)).await;
        }
    });
    let stream = TaskStream::stream();
    loop {
        while let Some(task) = stream.get_task() {
            task.run();
        }
        cortex_m::asm::wfi();
    }
}
